package com.guguguweb.rpc.net.server.Handler;

import com.esotericsoftware.reflectasm.MethodAccess;
import com.guguguweb.rpc.entity.RpcRequest;
import com.guguguweb.rpc.entity.RpcResponse;
import com.guguguweb.rpc.enumeration.ResponseCode;
import com.guguguweb.rpc.provider.ServiceProvider;
import lombok.Synchronized;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class LocalRequestHandler {


    private static final Logger logger = LoggerFactory.getLogger(LocalRequestHandler.class);


    private final ServiceProvider serviceProvider;


    private final Map<Class,MethodAccess> cahce = new ConcurrentHashMap<>();

    public LocalRequestHandler(ServiceProvider serviceProvider){
        this.serviceProvider = serviceProvider;
    }


    public Object handle(RpcRequest request){
        Object service = serviceProvider.getServiceProvider(request.getInterfaceName());
        if(service == null){
            return RpcResponse.fail(ResponseCode.ClASS_NOT_FOUND, request.getRequestId());
        }
        return invokeTargetMethod(request, service);
    }

    private Object invokeTargetMethod(RpcRequest rpcRequest,Object service) {
        Object result;
        try{
            MethodAccess methodAccess = getMethodAccess(service.getClass());
            result = methodAccess.invoke(service, rpcRequest.getMethodName(), rpcRequest.getParamTypes(), rpcRequest.getParameters());
            logger.debug("服务：{}成功调用方法：{}", rpcRequest.getInterfaceName(), rpcRequest.getMethodName());
        }catch (Exception e){
            //方法调用失败
            return RpcResponse.fail(ResponseCode.METHOD_NOT_FOUND, rpcRequest.getRequestId());
        }
        //方法调用成功
        return RpcResponse.success(result, rpcRequest.getRequestId());
    }

    private MethodAccess getMethodAccess(Class clazz){
        MethodAccess methodAccess = cahce.get(clazz);
        if(methodAccess != null){
            return methodAccess;
        }
        synchronized(clazz){
            methodAccess = cahce.get(clazz);
            if(methodAccess != null){
                return methodAccess;
            }
            methodAccess = MethodAccess.get(clazz);
            cahce.put(clazz, methodAccess);
        }
        return methodAccess;
    }

}
